package com.basker.pisces.springcloud.signature;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import com.basker.pisces.common.enums.SignatureAlgorithmType;
import com.basker.pisces.service.annotation.EnableSignValidation;
import com.basker.pisces.spring.SpringApplicationContext;
import com.basker.pisces.springcloud.GlobalAspect;
import com.basker.pisces.springcloud.utils.RequestUtils;
import com.basker.pisces.springcloud.utils.SignatureUtils;

import feign.RequestInterceptor;
import feign.RequestTemplate;

/**
 * 签名生成拦截器，基于AOP，拦截所有{@link EnableSignValidation}注解的方法，在执行前，生成签名并添加到消息头中。
 *
 * @author hangwen
 */
@Aspect
public class SignatureGenerateInterceptor extends GlobalAspect implements RequestInterceptor {

    /**
     * feign拦截器，将之前生成好的签名添加到消息头中
     */
    @Override
    public void apply(RequestTemplate template) {
        Signature signature = SignatureHolder.getSignature();
        if (signature == null) {
            return;
        }

        RequestUtils.putSignatureToRequest(signature, template);
    }

    /**
     * feign的工作线程来自于一个线程池，所以请求执行完成后（无论是成功还是出现异常），都需要清除当前线程上下文中的签名
     *
     * @param signValidation
     */
    @After("@annotation(signValidation)&&!restController()")
    public void clearSignatureHolder(EnableSignValidation signValidation) {
        SignatureHolder.clear();
    }

    /**
     * 满足切入点：方法需要签名 并且 当前类不是restController,说明复合条件的是客户端，端执行前生成签名
     *
     * @param joinPoint
     * @param signValidation
     */
    @Before("@annotation(signValidation)&&!restController()")
    public void generateSignature(JoinPoint joinPoint, EnableSignValidation signValidation) {
        SignatureAlgorithmType signType = signValidation.signType();
        String appId = SpringApplicationContext.getInstance().getName();
        long timestamp = System.currentTimeMillis();
        Object[] args = joinPoint.getArgs();
        Object bodyArg = args.length > 0 ? args[0] : null;

        Signature signature = SignatureUtils.generateSignature(signType, appId, timestamp, bodyArg);
        SignatureHolder.setSignature(signature, true);
    }

}
